#include "StdAfx.h"
#include "IWFMeshStructure.h"
#include "..\Libs\libIWF\libIWF.h"
#include "..\Libs\libIWF\iwfFile.h"
#include "..\Libs\libIWF\iwfObjects.h"
#include "GraphicsController.h"


////////////////////////////////////////////////////////////////////////////////

DWORD CIWFMeshVertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;

////////////////////////////////////////////////////////////////////////////////

void CTextureGroup::render(IDirect3DDevice9& d3Device,
                           std::vector<IDirect3DTexture9*>& textures,
                           std::vector<D3DMATERIAL9>& materials)
{
   d3Device.SetTexture(0, (m_textureIdx >= 0) ? textures[m_textureIdx] : NULL);
   
   for (UINT materialGroupIdx = 0; materialGroupIdx < m_materialGroups.size(); ++materialGroupIdx)
   {
      CMaterialGroup* materialGroup = m_materialGroups[materialGroupIdx];
      materialGroup->render(d3Device, materials);
   }

   d3Device.SetTexture(0, NULL);
}

////////////////////////////////////////////////////////////////////////////////

void CMaterialGroup::render(IDirect3DDevice9& d3Device, 
                            std::vector<D3DMATERIAL9>& materials)
{
   if ((m_indexBuffer == NULL) || (m_vertexCount == 0) || (m_indices.size() == 0)) {return;}

   d3Device.SetMaterial(&materials[m_materialIdx]);
   d3Device.SetIndices(m_indexBuffer);
   d3Device.DrawIndexedPrimitive(D3DPT_TRIANGLELIST, m_vertexStart, 0, m_vertexCount, 0, m_indices.size() / 3); 
}

////////////////////////////////////////////////////////////////////////////////

CTextureGroup::CTextureGroup(CGraphicsController& graphicsController,
                             long textureIdx)
      : m_graphicsController(graphicsController),
      m_textureIdx(textureIdx)
{
}

////////////////////////////////////////////////////////////////////////////////

CTextureGroup::~CTextureGroup()
{
   for (UINT i = 0; i < m_materialGroups.size(); i++)
   {
      delete m_materialGroups[i];
   }
   m_materialGroups.clear();
}

////////////////////////////////////////////////////////////////////////////////

CMaterialGroup* CTextureGroup::findMaterialGroup(UINT materialIdx)
{
   CMaterialGroup* grp = NULL;

   for (UINT i = 0; i < m_materialGroups.size(); ++i)
   {
      if (*(m_materialGroups[i]) == materialIdx)
      {
         grp = m_materialGroups[i];
         break;
      }
   }

   return grp;
}

////////////////////////////////////////////////////////////////////////////////

void CTextureGroup::addMaterialGroup(CMaterialGroup* materialGroup) 
{
   if (materialGroup == NULL)
   {
      throw std::invalid_argument(
         std::string("NULL pointer instead a CMaterialGroup instance"));
   }
   m_materialGroups.push_back(materialGroup);
}

////////////////////////////////////////////////////////////////////////////////

void CTextureGroup::buildBuffers()
{
   // build the buffers for all the texture groups
   for (ULONG materialGroupIdx = 0; materialGroupIdx < m_materialGroups.size(); ++materialGroupIdx)
   {
      CMaterialGroup* materialGroup = m_materialGroups[materialGroupIdx];
      materialGroup->buildBuffers();
   }
}

////////////////////////////////////////////////////////////////////////////////

CMaterialGroup::CMaterialGroup(CGraphicsController& graphicsController,
                               UINT materialIdx,
                               UINT vertexStart)
      : m_graphicsController(graphicsController),
      m_materialIdx(materialIdx),
      m_indexBuffer(NULL),
      m_vertexStart(vertexStart),
      m_vertexCount(0)
{
}

////////////////////////////////////////////////////////////////////////////////

CMaterialGroup::~CMaterialGroup()
{
   if (m_indexBuffer != NULL)
   {
      m_indexBuffer->Release();
      m_indexBuffer = NULL;
   }
}

////////////////////////////////////////////////////////////////////////////////

void CMaterialGroup::addSurface(iwfSurface* surface, UINT lightGroupVertexCount)
{
   // increate number of vertices covered by this group
   m_vertexCount += surface->VertexCount;

   // generate indices
   UINT vertexStart = lightGroupVertexCount - m_vertexStart;
   if (surface->IndexCount > 0)
   {
      ULONG indexType = surface->IndexFlags & INDICES_MASK_TYPE;

      // Interpret indices (we want them in tri-list format)
      switch (indexType)
      {
      case INDICES_TRILIST:
         {
            for (UINT i = 0; i < surface->IndexCount; i++) 
            {
               m_indices.push_back(surface->Indices[i] + vertexStart);
            }
            break;
         }

      case INDICES_TRISTRIP:
         {
            // Index in strip order
            for (long i = 0; i < surface->IndexCount - 2; i++)
            {
               // Starting with triangle 0.
               // Is this an 'Odd' or 'Even' triangle
               if ((i % 2) == 0)
               {
                  m_indices.push_back(surface->Indices[i] + vertexStart);
                  m_indices.push_back(surface->Indices[i + 1] + vertexStart);
                  m_indices.push_back(surface->Indices[i + 2] + vertexStart);
               } // End if 'Even' triangle
               else
               {
                  m_indices.push_back(surface->Indices[i]  + vertexStart);
                  m_indices.push_back(surface->Indices[i + 2] + vertexStart);
                  m_indices.push_back(surface->Indices[i + 1] + vertexStart);
               } // End if 'Odd' triangle
            }

            break;
         }

      case INDICES_TRIFAN:
         {
            // Index in fan order.
            for (long i = 1; i < surface->VertexCount - 1; i++)
            {
               m_indices.push_back(surface->Indices[0] + vertexStart);
               m_indices.push_back(surface->Indices[i] + vertexStart);
               m_indices.push_back(surface->Indices[i + 1] + vertexStart);
            }

            break;
         }
      }
   }
   else
   {
      ULONG vertexType = surface->VertexFlags & VERTICES_MASK_TYPE;

      // Interpret vertices (we want our indices in tri-list format)
      switch (vertexType)
      {
      case VERTICES_TRILIST:
         {
            // Straight fill
            for (UINT i = 0; i < surface->VertexCount; i++)
            {
               m_indices.push_back(i + vertexStart);
            }
            break;
         }

      case VERTICES_TRISTRIP:

         // Index in strip order
         for (long i = 0; i < surface->VertexCount - 2; i++)
         {
            // Starting with triangle 0.
            // Is this an 'Odd' or 'Even' triangle
            if ( (i % 2) == 0 )
            {
               m_indices.push_back(i + vertexStart);
               m_indices.push_back(i + 1 + vertexStart);
               m_indices.push_back(i + 2 + vertexStart);
            } // End if 'Even' triangle
            else
            {
               m_indices.push_back(i + vertexStart);
               m_indices.push_back(i + 2 + vertexStart);
               m_indices.push_back(i + 1 + vertexStart);
            } // End if 'Odd' triangle

         }

         break;

      case VERTICES_TRIFAN:

         // Index in fan order.
         for (long i = 1; i < surface->VertexCount - 1; i++)
         {
            m_indices.push_back(vertexStart);
            m_indices.push_back(i + vertexStart);
            m_indices.push_back(i + 1 + vertexStart);
         }

         break;

      }
   }
}

////////////////////////////////////////////////////////////////////////////////

void CMaterialGroup::buildBuffers()
{
   if (m_indexBuffer != NULL)
   {
      m_indexBuffer->Release();
   }

   m_indexBuffer = m_graphicsController.createIndexBuffer(sizeof(WORD) * m_indices.size(),
                                              D3DUSAGE_WRITEONLY,
                                              D3DFMT_INDEX16,
                                              D3DPOOL_MANAGED);

   WORD* pIndex = NULL;
   HRESULT res = m_indexBuffer->Lock(0, sizeof(WORD) * m_indices.size(), 
                                     (void**)&pIndex, D3DLOCK_DISCARD);
   if (FAILED(res))
   {
      throw std::logic_error(
         std::string("Cannot lock a material group index buffer"));
   }

   for (UINT i = 0; i < m_indices.size(); i++)
   {
      *pIndex++ = static_cast<WORD> (m_indices[i]);
   }

   m_indexBuffer->Unlock();
}

////////////////////////////////////////////////////////////////////////////////

bool CMaterialGroup::operator==(UINT materialIdx) const
{
   return m_materialIdx == materialIdx;
}

////////////////////////////////////////////////////////////////////////////////
